home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue45 / Alfresco / AALZHash.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-11-02  |  8.5 KB  |  291 lines

  1. {*********************************************************}
  2. {* AALZHash                                              *}
  3. {* Copyright (c) Julian M Bucknall 1998-1999             *}
  4. {* All rights reserved.                                  *}
  5. {*********************************************************}
  6. {* Algorithms Alfresco LZ77 unit - Hash Table            *}
  7. {*********************************************************}
  8.  
  9. {Note: this unit is released as freeware. In other words, you are free
  10.        to use this unit in your own applications, however I retain all
  11.        copyright to the code. JMB}
  12.  
  13. unit AALZHash;
  14.  
  15. interface
  16.  
  17. uses
  18.   SysUtils,
  19.   Classes,
  20.   AALZBase;
  21.  
  22. {Notes: Although a pretty standard hash table with chaining this
  23.         particular version has several changes to make it more
  24.         applicable to LZ compression.
  25.         Firstly all key strings are 3 characters long. The hash
  26.         function is the three characters of the key as the three
  27.         lowest bytes of a longint, with the upper byte set to zero.
  28.         Secondly the items in the hash table comprise the 3 character
  29.         key string, together with the input stream offset where the
  30.         string appears.
  31.         Thirdly the offset is used as a least recently used indicator:
  32.         when a key is searched for, older nodes in the chains are
  33.         freed automatically. Older nodes equate to lower offsets.
  34.  
  35.         Although a delete method could be written, because of the way
  36.         this hash table is designed to be used, there is none.
  37.  
  38.         All nodes in the hash table are 12 bytes long (next pointer,
  39.         key string and offset) and so a node manager is used for
  40.         space and speed efficiency reasons.
  41.         }
  42.  
  43. type
  44.   ThtLZKeyEnumProc = procedure (aExtraData : pointer;
  45.                           const aKey       : TaaLZKey;
  46.                                 aOffset    : longint);
  47.  
  48.   PaaLZHashNode = ^TaaLZHashNode;
  49.   TaaLZHashNode = packed record
  50.     hnNext   : PaaLZHashNode;
  51.     hnKey    : TaaLZKey;
  52.     hnOffset : longint;
  53.   end;
  54.  
  55.   TaaLZHashTable = class
  56.     {-a hash table for LZ77 compression}
  57.     private
  58.       htlArray     : TList;
  59.     protected
  60.       procedure htlFreeChain(aFromNode : PaaLZHashNode; aIsParent : boolean);
  61.     public
  62.       constructor Create;
  63.         {-constructor to create a hash table for LZ data compression}
  64.       destructor Destroy; override;
  65.         {-destructor to destroy the hash table}
  66.  
  67.       procedure Empty;
  68.         {-delete all elements in the hash table and reset it to empty}
  69.       function FindAll(const aKey       : TaaLZKey;
  70.                              aCutOffset : longint;
  71.                              aAction    : ThtLZKeyEnumProc;
  72.                              aExtraData : pointer) : boolean;
  73.         {-find all the elements defined by aKey; Call aAction for each
  74.           one found. All nodes encountered less than aCutOffset are
  75.           automatically removed. Returns true if there was at least
  76.           one element found with key aKey.}
  77.       procedure Insert(const aKey    : TaaLZKey;
  78.                              aOffset : longint);
  79.         {-insert a new element defined by aKey with its associated
  80.           offset aOffset. It is assumed that Insert is called with
  81.           strictly increasing values of aOffset.}
  82.   end;
  83.  
  84. implementation
  85.  
  86. {$DEFINE UseNodeManager}
  87.  
  88. const
  89.   HashTableSize = 521; {a prime}
  90.  
  91. {===SingleNodeManager================================================}
  92. const
  93.   PageNodeCount = 100;
  94. type
  95.   PsnmPage = ^TsnmPage;
  96.   TsnmPage = packed record
  97.     snmpNext  : PsnmPage;
  98.     snmpNodes : array [0..pred(PageNodeCount)] of TaaLZHashNode;
  99.   end;
  100. {--------}
  101. var
  102.   snmFreeList : PaaLZHashNode;
  103.   snmPageList : PsnmPage;
  104. {--------}
  105. procedure snmFreeNode(aNode : PaaLZHashNode);
  106. begin
  107.   {$IFDEF UseNodeManager}
  108.   {add the node to the top of the free list}
  109.   aNode^.hnNext := snmFreeList;
  110.   snmFreeList := aNode;
  111.   {$ELSE}
  112.   Dispose(aNode);
  113.   {$ENDIF}
  114. end;
  115. {--------}
  116. procedure snmAllocPage;
  117. var
  118.   NewPage : PsnmPage;
  119.   i       : integer;
  120. begin
  121.   {get a new page}
  122.   New(NewPage);
  123.   {add it to the current list of pages}
  124.   NewPage^.snmpNext := snmPageList;
  125.   snmPageList := NewPage;
  126.   {add all the nodes on the page to the free list}
  127.   for i := 0 to pred(PageNodeCount) do
  128.     snmFreeNode(@NewPage^.snmpNodes[i]);
  129. end;
  130. {--------}
  131. function snmAllocNode : PaaLZHashNode;
  132. begin
  133.   {$IFDEF UseNodeManager}
  134.   {if the free list is empty, allocate a new page of nodes}
  135.   if (snmFreeList = nil) then
  136.     snmAllocPage;
  137.   {return the first node on the free list}
  138.   Result := snmFreeList;
  139.   snmFreeList := Result^.hnNext;
  140.   {$ELSE}
  141.   New(Result);
  142.   {$ENDIF}
  143.   {$IFDEF InDebugMode}
  144.   FillChar(Result^, sizeof(Result^), $CC);
  145.   {$ENDIF}
  146. end;
  147. {====================================================================}
  148.  
  149.  
  150. {===Helper routines==================================================}
  151. procedure RaiseException(const S : string);
  152. begin
  153.   raise Exception.Create(S);
  154. end;
  155. {====================================================================}
  156.  
  157.  
  158. {===TaaLZHashTable===============================================}
  159. constructor TaaLZHashTable.Create;
  160. begin
  161.   inherited Create;
  162.   htlArray := TList.Create;
  163.   htlArray.Count := HashTableSize;
  164. end;
  165. {--------}
  166. destructor TaaLZHashTable.Destroy;
  167. begin
  168.   if (htlArray <> nil) then begin
  169.     Empty;
  170.     htlArray.Free;
  171.   end;
  172.   inherited Destroy;
  173. end;
  174. {--------}
  175. procedure TaaLZHashTable.Empty;
  176. var
  177.   Inx : integer;
  178.   ChainHead : PaaLZHashNode;
  179. begin
  180.   for Inx := 0 to pred(HashTableSize) do begin
  181.     ChainHead := PaaLZHashNode(htlArray[Inx]);
  182.     if (ChainHead <> nil) then begin
  183.       htlFreeChain(ChainHead, false);
  184.       htlArray[Inx] := nil;
  185.     end;
  186.   end;
  187. end;
  188. {--------}
  189. function TaaLZHashTable.FindAll(const aKey       : TaaLZKey;
  190.                                       aCutOffset : longint;
  191.                                       aAction    : ThtLZKeyEnumProc;
  192.                                       aExtraData : pointer) : boolean;
  193. var
  194.   Inx : integer;
  195.   Temp : PaaLZHashNode;
  196.   Dad  : PaaLZHashNode;
  197. begin
  198.   {assume we don't find any}
  199.   Result := false;
  200.   {calculate the hash table index for this key}
  201.   Inx := (aKey.AsLong shr 8) mod HashTableSize;
  202.   {wander along the chain at this index}
  203.   Dad := nil;
  204.   Temp := PaaLZHashNode(htlArray[Inx]);
  205.   while (Temp <> nil) do begin
  206.     {if this node has an offset that is less than the cutoff offset,
  207.      then remove the rest of this chain and exit}
  208.     if (Temp^.hnOffset < aCutOffset) then begin
  209.       if (Dad = nil) then begin
  210.         htlFreeChain(Temp, false);
  211.         htlArray[Inx] := nil;
  212.       end
  213.       else
  214.         htlFreeChain(Dad, true);
  215.       Exit;
  216.     end;
  217.     {if the node's key matches our key, call the action routine}
  218.     if (Temp^.hnKey.AsLong = aKey.AsLong) then begin
  219.       Result := true;
  220.       aAction(aExtraData, aKey, Temp^.hnOffset);
  221.     end;
  222.     {advance to the next node}
  223.     Dad := Temp;
  224.     Temp := Dad^.hnNext;
  225.   end;
  226. end;
  227. {--------}
  228. procedure TaaLZHashTable.htlFreeChain(aFromNode : PaaLZHashNode;
  229.                                       aIsParent : boolean);
  230. var
  231.   Temp : PaaLZHashNode;
  232. begin
  233.   if aIsParent then begin
  234.     Temp := aFromNode^.hnNext;
  235.     aFromNode^.hnNext := nil;
  236.   end
  237.   else
  238.     Temp := aFromNode;
  239.   while (Temp <> nil) do begin
  240.     aFromNode := Temp^.hnNext;
  241.     snmFreeNode(Temp);
  242.     Temp := aFromNode;
  243.   end;
  244. end;
  245. {--------}
  246. procedure TaaLZHashTable.Insert(const aKey : TaaLZKey;
  247.                                       aOffset : longint);
  248. var
  249.   Inx     : integer;
  250.   NewNode : PaaLZHashNode;
  251. begin
  252.   {calculate the hash table index for this key}
  253.   Inx := (aKey.AsLong shr 8) mod HashTableSize;
  254.   {allocate a new node and insert at the head of the chain at this
  255.    index in the hash table; this ensures that the nodes in the chain
  256.    are in reverse order of offset value}
  257.   NewNode := snmAllocNode;
  258.   NewNode^.hnKey := aKey;
  259.   NewNode^.hnOffset := aOffset;
  260.   NewNode^.hnNext := htlArray[Inx];
  261.   htlArray[Inx] := NewNode;
  262. end;
  263. {====================================================================}
  264.  
  265. procedure FinalizeUnit; far;
  266. var
  267.   STemp : PsnmPage;
  268. begin
  269.   {destroy all the single node pages}
  270.   STemp := snmPageList;
  271.   while (STemp <> nil) do begin
  272.     snmPageList := STemp^.snmpNext;
  273.     Dispose(STemp);
  274.     STemp := snmPageList;
  275.   end;
  276. end;
  277.  
  278. initialization
  279.   snmFreeList := nil;
  280.   snmPageList := nil;
  281.   {$IFDEF Windows}
  282.   AddExitProc(FinalizeUnit);
  283.   {$ENDIF}
  284.  
  285. {$IFDEF WIN32}
  286. finalization
  287.   FinalizeUnit;
  288. {$ENDIF}
  289.  
  290. end.
  291.